#    Gezilla Mozilla Development gezilla plugin
#    Copyright (C) 2009  Lucas Alvares Gomes <lucasagomes@gmail.com>
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 
#    02110-1301  USA


import gedit
import gtk
import gtksourceview2
import gtk.glade
import os
from uuid import uuid4


ui_string = """<ui>
  <menubar name="MenuBar">
    <menu name="FileMenu" action="File">
      <placeholder name="FileOps_1">
        <menuitem name="New XPCOM Component(Python)" action="NewXPCOMComponentPython"/>
        <menuitem name="New XPCOM Component(Javascript)" action="NewXPCOMComponentJavascript"/>
        <menuitem name="New XPCOM Component(C++)" action="NewXPCOMComponentCpp"/>
        <menuitem name="New IDL" action="NewIDL"/>
        <menuitem name="New XUL Window" action="NewXULWindow"/>
      </placeholder>
    </menu>
  </menubar>
</ui>
"""

DIALOG = os.path.join(os.path.dirname(__file__), "dialog.glade")
DIALOG_CPP = os.path.join(os.path.dirname(__file__), "dialog_cpp.glade")

class Gezilla_Stuff:
    def __init__(self,plugin,window):
        self.default_company_name = ""
        self.default_gecko_xpidl_path = ""
        self.default_gecko_idl_path = ""
        self._window = window
        self._plugin = plugin
        self._insert_menu()


    def deactivate(self):
        self._remove_menu()
        self.window = None
        self.plugin =  None


    def update_ui(self):
        self._action_group.set_sensitive(True)

    def _insert_menu(self):
        manager = self._window.get_ui_manager()
        self._action_group = gtk.ActionGroup("GezillaActions")
        self._action_group.add_actions([("NewXPCOMComponentPython", None, "New XPCOM Component(Python)", '<Control><Shift>x',"New XPCOM Component(Python)",self.on_open_dialog)]) 
        self._action_group.add_actions([("NewXPCOMComponentJavascript", None, "New XPCOM Component(Javascript)", '<Control><Shift>j',"New XPCOM Component(Javascript)",self.on_open_dialog)])
        self._action_group.add_actions([("NewXPCOMComponentCpp", None, "New XPCOM Component(C++)", '<Control><Shift>c',"New XPCOM Component(C++)",self.on_open_dialog_cpp)])
        self._action_group.add_actions([("NewIDL", None, "New IDL", '<Control><Shift>i',"New IDL",self.on_open_dialog)]) 
        self._action_group.add_actions([("NewXULWindow", None, "New XUL Window", None,"New XUL Window",self.on_create_new_xul_window)])
        
        manager.insert_action_group(self._action_group, 0)

        #Add to ui and get a the id
        self._ui_id = manager.add_ui_from_string(ui_string)

    def _remove_menu(self):
        manager = self._window.get_ui_manager()
        manager.remove_ui(self._ui_id)
        manager.remove_action_group(self._action_group)
        manager.ensure_update()

    def create_new_xpcom_component_python(self, component_name, company_name, interface_name):
        doc = self._window.create_tab(1)
        doc = self._window.get_active_document()
        if not doc:
            return

        try:
            language_manager = gtksourceview2.LanguageManager()
            view = self._window.get_active_view()
            buffer = view.get_buffer()
            language = language_manager.get_language("python")
            buffer.set_language(language)
        except:
            pass

        new_uuid = str(uuid4())
        xpcom_python_string = '#!/usr/bin/env python\n\n\nfrom xpcom import components, COMException, ServerException, nsError\n\nclass %s:\n\t_com_interfaces_ = [components.interfaces.%s]\n\t_reg_clsid_ = "{%s}"\n\t_reg_contractid_ = "@%s/%s;1"\n\t_reg_desc_ = "..."\n\n\t'%(component_name, interface_name, new_uuid, company_name, component_name)
        doc.set_text(xpcom_python_string)


    def create_new_idl(self, component_name, interface_name):
        doc = self._window.create_tab(1)
        doc = self._window.get_active_document()
        if not doc:
            return

        try:
            language_manager = gtksourceview2.LanguageManager()
            view = self._window.get_active_view()
            buffer = view.get_buffer()
            language = language_manager.get_language("idl")
            buffer.set_language(language)
        except:
            pass

        new_uuid = str(uuid4())
        idl_string = '#include "nsISupports.idl"\n\n[scriptable, uuid(%s)]\ninterface %s: nsISupports {\n\n\n\n};'%(new_uuid, interface_name)
        doc.set_text(idl_string)

    def on_create_new_xul_window(self, action):
        doc = self._window.create_tab(1)
        doc = self._window.get_active_document()
        if not doc:
            return

        try:
            language_manager = gtksourceview2.LanguageManager()
            view = self._window.get_active_view()
            buffer = view.get_buffer()
            language = language_manager.get_language("xml")
            buffer.set_language(language)
        except:
            pass

        xul_window_string = '<?xml version="1.0"?>\n<!DOCTYPE window PUBLIC "-//MOZILLA//DTD XUL V1.0//EN" "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">\n\n<?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?>\n\n<window id=""\n\twindowtype=""\n\ttitle=""\n\txmlns:html="http://www.w3.org/1999/xhtml"\n\txmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"\n\txmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"\n\twidth="800"\n\theight="600"\n\t>\n\n\n</window>'
        doc.set_text(xul_window_string)

    def create_new_xpcom_component_javascript(self, component_name, company_name, interface_name):
        doc = self._window.create_tab(1)
        doc = self._window.get_active_document()
        if not doc:
            return

        try:
            language_manager = gtksourceview2.LanguageManager()
            view = self._window.get_active_view()
            buffer = view.get_buffer()
            language = language_manager.get_language("js")
            buffer.set_language(language)
        except:
            pass
        
        new_uuid = str(uuid4())
        xpcom_javascript_string = 'var %sModule = new Object();\n\nconst %s_CONTRACTID     = "@%s/%s;1";\nconst %s_CID        = Components.ID("{%s}");\n\nfunction %s()\n{\n}\n%s.prototype = {\n\tQueryInterface: function(iid) {\n\tif (!iid.equals(%s) &&\n\t\t!iid.equals(nsISupports))\n\t\tthrow Components.results.NS_ERROR_NO_INTERFACE;\n\treturn this;\n\t}\n}\n\n\n%sModule.registerSelf =\nfunction (compMgr, fileSpec, location, type)\n{\n\tcompMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);\n\tcompMgr.registerFactoryLocation(%s_CID, \n\t"%s Component",\n\t%s_CONTRACTID, \n\tfileSpec, \n\tlocation, \n\ttype);\n}\n\n%sModule.getClassObject =\nfunction (compMgr, cid, iid) {\n\tif (!cid.equals(DIALOGPROXY_CID))\n\tthrow Components.results.NS_ERROR_NO_INTERFACE;\n\n\tif (!iid.equals(Components.interfaces.nsIFactory))\n\tthrow Components.results.NS_ERROR_NOT_IMPLEMENTED;\n\n\treturn %sFactory;\n}\n\n%sModule.canUnload =\nfunction(compMgr)\n{\n\treturn true;\n}\n\n/* factory object */\nvar %sFactory = new Object();\n\n%sFactory.createInstance =\nfunction (outer, iid) {\n\tif (outer != null)\n\tthrow Components.results.NS_ERROR_NO_AGGREGATION;\n\n\treturn (new %s()).QueryInterface(iid);\n}\n\n/* entrypoint */\nfunction NSGetModule(compMgr, fileSpec) {\n\treturn %sModule;\n}'%(component_name, component_name, company_name, component_name, component_name, new_uuid, component_name, component_name, interface_name, component_name, component_name, component_name, component_name, component_name, component_name, component_name, component_name, component_name, component_name, component_name)
        doc.set_text(xpcom_javascript_string)

    def create_new_xpcom_component_cpp(self, gecko_xpidl_path, gecko_idl_path, idl_path, component_name, company_name):
        
        #Get the output header file path
        h_path = idl_path.split('.')[:-1][0] + '.h'
        
        saida = os.system(gecko_xpidl_path + " -m header -I "+ gecko_idl_path + " -e "+ h_path +" " + idl_path)
        if saida != 0:
            self.alert("Error generating the interface header")
            return #ERROR
        
        #Create this tab to load the header file
        doc = self._window.create_tab(1)
        doc = self._window.get_active_document()
        
        if not doc:
            return
        
        #Load de interface header file (geckp created)
        doc.load('file://'+h_path, gedit.encoding_get_current(), 1, True)
        
        self.create_header_file(h_path, component_name, company_name)
        self.create_implementation_file(h_path, component_name)
        self.create_module_definitions_file(h_path, component_name)
    
    
    #************************
    #Create the MyComponent.h
    #************************
    def create_header_file(self, header_path, component_name, company_name):
        
        doc = self._window.create_tab(1)
        doc = self._window.get_active_document()
        
        if not doc:
            return
        
        fullheader = "#ifndef _"+component_name.upper()+"_H_\n"
        fullheader += "#define _"+component_name.upper()+"_H_\n\n"
        fullheader += "#include \""+header_path.split('/')[-1]+"\"\n"
        fullheader += "#define "+component_name.upper()+"_CONTRACTID \"@%s/%s;1\"\n"%(company_name,component_name)
        fullheader += "#define "+component_name.upper()+"_CLASSNAME \" \"\n"
        
        new_uuid = str(uuid4())
        new_uuid = new_uuid.split('-')
        new_uuid = "{ 0x%s, 0x%s, 0x%s, { 0x%s, 0x%s, 0x%s, 0x%s, 0x%s, 0x%s, 0x%s, 0x%s } }"%(new_uuid[0], new_uuid[1], new_uuid[2], new_uuid[3][:2], new_uuid[3][2:4], new_uuid[4][:2], new_uuid[4][2:4], new_uuid[4][4:6], new_uuid[4][6:8], new_uuid[4][8:10], new_uuid[4][10:12] )
        
        fullheader += "#define "+component_name.upper()+"_CID %s\n\n"%(new_uuid)
        
        f = open(header_path)
        lines = f.readlines()
        
        flag = False
        cache_header = ""
        for line in lines:
            if line[:17] == "/* Header file */":
                flag = True
            if line[:25] == "/* Implementation file */":
                flag = False
            if flag:
                cache_header += line
        
        f.close()
        fullheader += cache_header.replace("_MYCLASS_","%s"%(component_name))
        fullheader += "#endif"
        
        try:
            language_manager = gtksourceview2.LanguageManager()
            view = self._window.get_active_view()
            buffer = view.get_buffer()
            language = language_manager.get_language("cpp")
            buffer.set_language(language)
        except:
            pass
        
        doc.set_text(fullheader)
        
        uri = os.sep.join(header_path.split('/')[:-1])+os.sep+component_name+".h"
        #Save/Create the file
        doc.save_as("file://"+uri, gedit.encoding_get_current(), gedit.DOCUMENT_SAVE_PRESERVE_BACKUP)
    
    #**************************
    #Create the MyComponent.cpp
    #**************************  
    def create_implementation_file(self, header_path, component_name):

        doc = self._window.create_tab(1)
        doc = self._window.get_active_document()
        
        if not doc:
            return
        
        fullimplementation = "#include \"%s\"\n\n"%(component_name+".h")
        
        f = open(header_path)
        lines = f.readlines()
        
        flag = False
        cache_implementation = ""
        for line in lines:
            if line[:25] == "/* Implementation file */":
                flag = True
            if line[:43] == "/* End of implementation class template. */":
                flag = False
            if flag:
                cache_implementation += line
        
        try:
            language_manager = gtksourceview2.LanguageManager()
            view = self._window.get_active_view()
            buffer = view.get_buffer()
            language = language_manager.get_language("cpp")
            buffer.set_language(language)
        except:
            pass
        
        f.close()
        fullimplementation += cache_implementation.replace("_MYCLASS_","%s"%(component_name))
        
        doc.set_text(fullimplementation)
        
        uri = os.sep.join(header_path.split('/')[:-1])+os.sep+component_name+".cpp"
        #Save/Create the file
        doc.save_as("file://"+uri, gedit.encoding_get_current(), gedit.DOCUMENT_SAVE_PRESERVE_BACKUP)

    #************************
    #Create the MyComponentModule.cpp
    #************************
    def create_module_definitions_file(self,header_path, component_name):
        
        doc = self._window.create_tab(1)
        doc = self._window.get_active_document()
        
        if not doc:
            return
        
        fulldefinitions = "#include \"nsIGenericFactory.h\"\n"
        fulldefinitions += "#include \"%s\"\n\n"%(component_name+".h")
        fulldefinitions += "NS_GENERIC_FACTORY_CONSTRUCTOR(%s)\n\n"%(component_name)
        
        upper_name = component_name.upper()
        fulldefinitions += "static nsModuleComponentInfo components[] =\n{\n\t{\n\t\t%s_CLASSNAME, \n\t\t%s_CID, \n\t\t%s_CONTRACTID,\n\t\t%sConstructor,\n\t}\n};\n\n"%(upper_name, upper_name, upper_name, component_name)
        fulldefinitions += "NS_IMPL_NSGETMODULE(\"%ssModule\", components)"%(component_name)
        
        doc.set_text(fulldefinitions)
        
        uri = os.sep.join(header_path.split('/')[:-1])+os.sep+component_name+"Module.cpp"
        #Save/Create the file
        doc.save_as("file://"+uri, gedit.encoding_get_current(), gedit.DOCUMENT_SAVE_PRESERVE_BACKUP)

    def alert(self, message):
        dialog = gtk.MessageDialog(
        parent         = None,
        flags          = gtk.DIALOG_DESTROY_WITH_PARENT,
        type           = gtk.MESSAGE_INFO,
        buttons        = gtk.BUTTONS_OK,
        message_format = message)
        dialog.set_title('Gezilla error!')
        dialog.connect('response', lambda dialog, response: dialog.destroy())
        
        dialog.show()
        
    def on_open_dialog(self, action):
        self.glade_xml = gtk.glade.XML(DIALOG)
        self.dialog = self.glade_xml.get_widget('dialog')
        self.dialog.connect('destroy', self.on_destroy)
        self.dialog.connect('key-press-event', self.on_key_press)
        self.dialog.show_all()

        txt_company = self.glade_xml.get_widget('txt_company')

        if len(self.default_company_name.strip()) > 0:
            txt_company.set_text(self.default_company_name)

        button_ok = self.glade_xml.get_widget('btn_ok')
        button_ok.connect('clicked', self.on_ok)
        
        button_cancel = self.glade_xml.get_widget('btn_cancel')
        button_cancel.connect('clicked', self.on_cancel)
        
        self.current_action = action.get_name()
    
    def on_open_dialog_cpp(self, action):
        self.glade_xml = gtk.glade.XML(DIALOG_CPP)
        self.dialog = self.glade_xml.get_widget('dialog_cpp')
        self.dialog.connect('destroy', self.on_destroy)
        self.dialog.connect('key-press-event', self.on_key_press)
        self.dialog.show_all()
        
        txt_gecko_xpidl = self.glade_xml.get_widget('txt_gecko_xpidl')
        txt_gecko_idl = self.glade_xml.get_widget('txt_gecko_idl')
        txt_company = self.glade_xml.get_widget('txt_company')

        if len(self.default_company_name.strip()) > 0:
            txt_company.set_text(self.default_company_name)
        
        #set defaults geckos
        txt_gecko_xpidl.set_text(self.default_gecko_xpidl_path)
        txt_gecko_idl.set_text(self.default_gecko_idl_path)
        
        button_ok = self.glade_xml.get_widget('btn_ok')
        button_ok.connect('clicked', self.on_ok)
        
        button_cancel = self.glade_xml.get_widget('btn_cancel')
        button_cancel.connect('clicked', self.on_cancel)
        
        self.current_action = action.get_name()

    def on_ok(self, widget):

        if self.current_action != "NewXPCOMComponentCpp":
            txt_company = self.glade_xml.get_widget('txt_company')
            txt_component = self.glade_xml.get_widget('txt_component')
            txt_interface = self.glade_xml.get_widget('txt_interface')
            chk_default_company_name = self.glade_xml.get_widget('chk_set_company_default')
            
            component_name = txt_component.get_text()
            company_name = txt_company.get_text()
            interface_name = txt_interface.get_text()

            if chk_default_company_name.get_active():
                self.default_company_name = company_name

            if len(component_name.strip()) <= 0: component_name = "Component"
            if len(interface_name.strip()) <= 0: interface_name = "InterfaceComponent"
            if len(company_name.strip()) <= 0: company_name = "Company.com"
            
            self.dialog.destroy()

            if self.current_action=="NewXPCOMComponentPython":
                self.create_new_xpcom_component_python(component_name, company_name, interface_name)
            elif self.current_action=="NewXPCOMComponentJavascript":
                self.create_new_xpcom_component_javascript(component_name, company_name, interface_name)
            elif self.current_action=="NewIDL":
                self.create_new_idl(component_name, interface_name)
        
        else:
            txt_gecko_xpidl = self.glade_xml.get_widget('txt_gecko_xpidl')
            txt_gecko_idl = self.glade_xml.get_widget('txt_gecko_idl')
            txt_idl = self.glade_xml.get_widget('txt_idl')
            txt_component = self.glade_xml.get_widget('txt_component')
            txt_company = self.glade_xml.get_widget('txt_company')
            chk_default_company_name = self.glade_xml.get_widget('chk_set_company_default')
            lbl_alert = self.glade_xml.get_widget('lbl_alert')
            
            gecko_xpidl_path = txt_gecko_xpidl.get_text()
            gecko_idl_path = txt_gecko_idl.get_text()
            idl_path = txt_idl.get_text()
            component_name = txt_component.get_text()
            company_name = txt_company.get_text()

            #Set defaults
            if chk_default_company_name.get_active():
                self.default_company_name = company_name
            
            self.default_gecko_xpidl_path = gecko_xpidl_path
            self.default_gecko_idl_path = gecko_idl_path

            if len(component_name.strip()) <= 0: component_name = "Component"
            if len(company_name.strip()) <= 0: company_name = "Company.com"
            
            ERROR = False
            if not os.path.isfile(gecko_xpidl_path):
                ERROR = True
            elif not os.path.isdir(gecko_idl_path):
                ERROR = True
            elif not os.path.isfile(idl_path):
                ERROR = True
            
            if ERROR:
                lbl_alert.set_label("Error! with paths")
                return
            
            self.dialog.destroy()
            self.create_new_xpcom_component_cpp(gecko_xpidl_path, gecko_idl_path, idl_path, component_name, company_name)

    def on_cancel(self, widget):
        self.dialog.destroy()

    def on_key_press(self, widget, event):
        if event.keyval == 65307: #ESC
            widget.destroy()
        
        if event.keyval == 65293: #Return
            self.on_ok(widget)

    def on_destroy(self, widget):
        widget.destroy()

'''

Class to Control the window

'''
class Gezilla_Window_Controller(gedit.Plugin):
    def __init__(self):
        gedit.Plugin.__init__(self)
        self._instances = {}

    def activate(self,window):
        self._instances[window] = Gezilla_Stuff(self, window)

    def deactivate(self,window):
        self._instances[window].deactivate()
        del self._instances[window]

    def update_ui(self,window):
        self._instances[window].update_ui()
